// -*- related-file-name: "../include/efont/findmet.hh" -*-

/* findmet.{cc,hh} -- find font metrics
 *
 * Copyright (c) 1998-2006 Eddie Kohler
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version. This program is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <efont/findmet.hh>
#include <efont/afmparse.hh>
#include <efont/afm.hh>
#include <efont/amfm.hh>
#include <efont/psres.hh>
#include <string.h>
#include <stdlib.h>
namespace Efont {

MetricsFinder::~MetricsFinder()
{
    if (_next)
	_next->_prev = _prev;
    if (_prev)
	_prev->_next = _next;
}

void
MetricsFinder::add_finder(MetricsFinder *new_finder)
{
    if (_next)
	_next->add_finder(new_finder);
    else {
	assert(!new_finder->_prev);
	new_finder->_prev = this;
	_next = new_finder;
    }
}

Metrics *
MetricsFinder::find_metrics(PermString name, ErrorHandler *errh)
{
    MetricsFinder *f = this;
    while (f) {
	Metrics *m = f->find_metrics_x(name, this, errh);
	if (m) return m;
	f = f->_next;
    }
    return 0;
}

AmfmMetrics *
MetricsFinder::find_amfm(PermString name, ErrorHandler *errh)
{
    MetricsFinder *f = this;
    while (f) {
	AmfmMetrics *m = f->find_amfm_x(name, this, errh);
	if (m) return m;
	f = f->_next;
    }
    return 0;
}

void
MetricsFinder::record(Metrics *m)
{
    record(m, m->font_name());
}

void
MetricsFinder::record(Metrics *m, PermString name)
{
    if (_next) _next->record(m, name);
}

void
MetricsFinder::record(AmfmMetrics *amfm)
{
    if (_next) _next->record(amfm);
}

Metrics *
MetricsFinder::find_metrics_x(PermString, MetricsFinder *, ErrorHandler *)
{
    return 0;
}

AmfmMetrics *
MetricsFinder::find_amfm_x(PermString, MetricsFinder *, ErrorHandler *)
{
    return 0;
}

Metrics *
MetricsFinder::try_metrics_file(const Filename &fn, MetricsFinder *finder,
				ErrorHandler *errh)
{
    if (fn.readable()) {
	Metrics *afm = AfmReader::read(fn, errh);
	if (afm) finder->record(afm);
	return afm;
    } else
	return 0;
}

AmfmMetrics *
MetricsFinder::try_amfm_file(const Filename &fn, MetricsFinder *finder,
			     ErrorHandler *errh)
{
    if (fn.readable()) {
	AmfmMetrics *amfm = AmfmReader::read(fn, finder, errh);
	if (amfm) finder->record(amfm);
	return amfm;
    } else
	return 0;
}


/*****
 * CacheMetricsFinder
 **/

CacheMetricsFinder::CacheMetricsFinder()
    : _metrics_map(-1), _amfm_map(-1)
{
}

CacheMetricsFinder::~CacheMetricsFinder()
{
    clear();
}

Metrics *
CacheMetricsFinder::find_metrics_x(PermString name, MetricsFinder *,
				   ErrorHandler *)
{
    int index = _metrics_map[name];
    return index >= 0 ? _metrics[index] : 0;
}

AmfmMetrics *
CacheMetricsFinder::find_amfm_x(PermString name, MetricsFinder *,
				ErrorHandler*)
{
    int index = _amfm_map[name];
    return index >= 0 ? _amfm[index] : 0;
}


void
CacheMetricsFinder::record(Metrics *m, PermString name)
{
    int index = _metrics.size();
    _metrics.push_back(m);
    _metrics_map.insert(name, index);
    m->use();
    MetricsFinder::record(m, name);
}

void
CacheMetricsFinder::record(AmfmMetrics *amfm)
{
    int index = _amfm.size();
    _amfm.push_back(amfm);
    _amfm_map.insert(amfm->font_name(), index);
    amfm->use();
    MetricsFinder::record(amfm);
}

void
CacheMetricsFinder::clear()
{
    for (int i = 0; i < _metrics.size(); i++)
	_metrics[i]->unuse();
    for (int i = 0; i < _amfm.size(); i++)
	_amfm[i]->unuse();
    _metrics.clear();
    _amfm.clear();
    _metrics_map.clear();
    _amfm_map.clear();
}


/*****
 * InstanceMetricsFinder
 **/

InstanceMetricsFinder::InstanceMetricsFinder(bool call_mmpfb)
    : _call_mmpfb(call_mmpfb)
{
}

Metrics *
InstanceMetricsFinder::find_metrics_instance(PermString name,
					     MetricsFinder *finder, ErrorHandler *errh)
{
    const char *underscore = strchr(name.c_str(), '_');
    PermString amfm_name =
	PermString(name.c_str(), underscore - name.c_str());
  
    AmfmMetrics *amfm = finder->find_amfm(amfm_name, errh);
    if (!amfm) return 0;
  
    MultipleMasterSpace *mmspace = amfm->mmspace();
    if (!mmspace->check_intermediate() && _call_mmpfb) {
	char *buf = new char[amfm->font_name().length() + 30];
	sprintf(buf, "mmpfb -q --amcp-info '%s'", amfm->font_name().c_str());
    
	FILE *f = popen(buf, "r");
	if (f) {
	    Filename fake("<mmpfb output>");
	    Slurper slurpy(fake, f);
	    AmfmReader::add_amcp_file(slurpy, amfm, errh);
	    pclose(f);
	}
    
	delete[] buf;
    }
  
    Vector<double> design = mmspace->default_design_vector();
    int i = 0;
    while (underscore[0] == '_' && underscore[1]) {
	double x = strtod(underscore + 1, const_cast<char **>(&underscore));
	mmspace->set_design(design, i, x, errh);
	i++;
    }
  
    Vector<double> weight;
    if (!mmspace->design_to_weight(design, weight, errh))
	return 0;
    Metrics *new_afm = amfm->interpolate(design, weight, errh);
    if (new_afm) {
	finder->record(new_afm);
	// What if the dimensions changed because the user specified out-of-range
	// dimens? We don't want to reinterpolate each time, so record the new
	// AFM under that name as well.
	if (new_afm->font_name() != name)
	    finder->record(new_afm, name);
    }
    return new_afm;
}

Metrics *
InstanceMetricsFinder::find_metrics_x(PermString name, MetricsFinder *finder,
				      ErrorHandler *errh)
{
    if (strchr(name.c_str(), '_'))
	return find_metrics_instance(name, finder, errh);
    else
	return 0;
}


/*****
 * PsresMetricsFinder
 **/

PsresMetricsFinder::PsresMetricsFinder(PsresDatabase *psres)
    : _psres(psres)
{
}

Metrics *
PsresMetricsFinder::find_metrics_x(PermString name, MetricsFinder *finder,
				   ErrorHandler *errh)
{
    return try_metrics_file
	(_psres->filename_value("FontAFM", name), finder, errh);
}

AmfmMetrics *
PsresMetricsFinder::find_amfm_x(PermString name, MetricsFinder *finder,
				ErrorHandler *errh)
{
    return try_amfm_file
	(_psres->filename_value("FontAMFM", name), finder, errh);
}


/*****
 * DirectoryMetricsFinder
 **/

DirectoryMetricsFinder::DirectoryMetricsFinder(PermString d)
    : _directory(d)
{
}

Metrics *
DirectoryMetricsFinder::find_metrics_x(PermString name, MetricsFinder *finder,
				       ErrorHandler *errh)
{
    Metrics *afm = try_metrics_file
	(Filename(_directory, permcat(name, ".afm")), finder, errh);
    if (!afm)
	afm = try_metrics_file
	    (Filename(_directory, permcat(name, ".AFM")), finder, errh);
    return afm;
}

AmfmMetrics *
DirectoryMetricsFinder::find_amfm_x(PermString name, MetricsFinder *finder,
				    ErrorHandler *errh)
{
    AmfmMetrics *amfm = try_amfm_file
	(Filename(_directory, permcat(name, ".amfm")), finder, errh);
    if (!amfm)
	amfm = try_amfm_file
	    (Filename(_directory, permcat(name, ".AMFM")), finder, errh);
    return amfm;
}

}
